<!--
 * @Author: 66xiezijian 1075010289@qq.com
 * @Date: 2024-03-29 11:55:25
 * @LastEditors: 谢子健 1075010289@qq.com
 * @LastEditTime: 2024-04-25 16:10:08
 * @FilePath: \llm_chat_front_end\src\views\pdf\components\upload_pdf\index.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<script setup lang="ts">
import { defineProps, nextTick, ref } from 'vue'
import { NButton, NDescriptions, NDescriptionsItem, NIcon, NInput, NModal, NP, NSelect, NSpace, NSwitch, NText, NUpload, NUploadDragger, useMessage } from 'naive-ui'
import type { UploadCustomRequestOptions, UploadInst } from 'naive-ui'
import { ArchiveOutline as ArchiveIcon } from '@vicons/ionicons5'
import type { IPdfMeta } from '@/views/pdf/DataModel.ts'
const props = defineProps<{
  isUserSelectSelf: boolean
  hubUuid: number
}>()
const emit = defineEmits<{
  (e: 'refreshComponent'): void
  (e: 'setShowSpin', isShow: boolean): void
  (e: 'setSpinDescription', description: string): void
}>()
const message = useMessage()
// 声明模拟框显示状态的变量
const isShowUploadModal = ref(false)

// 打开模拟框的方法
const openModal = () => {
  isShowUploadModal.value = true
}

// 填写doi自动解析元数据
const isShowAutoParseMeta = ref(false)

// 声明是否自定义填写元数据的变量
const isShowCustomMeta = ref(false)

const editedPdfMeta = ref<IPdfMeta>({
  pdfUuid: 0,
  hubUuid: 0,
  tags: [],
  doi: '',
  title: '',
  keyword: [],
  author: [],
  affiliation: [],
  publicTime: '',
  publicJournal: '',
  isConference: false,
  conferenceName: '',
  conferenceTime: '',
  staticUrl: '',
})

// note: 上传组件引用实例，使用实例时，组件就不可被隐藏，不然实例会变为null
const upload = ref<UploadInst | null>(null)
const uploadData = ref({})

const uploadFile = async (isAutoParse: boolean) => {
  console.log('上传文件')
  // 上传到个人知识库内，后端自己处理先解析元数据然后上传。
  const hubData = {
    hubUuid: props.hubUuid,
    isAutoParse,
    pdfMeta: editedPdfMeta.value,
  }
  // 里面传递json字符串
  uploadData.value = {
    postJSON: JSON.stringify(hubData),
  }

  // 需要更新组件数据之后再提交，等待一个时钟周期
  await nextTick()
  upload.value?.submit()
  // 阻塞页面，开始等待
  // 打开遮罩层
  emit('setShowSpin', true)
  emit('setSpinDescription', '正在上传文件...')
}

// 上传到个人知识库内
const uploadToPersonalHub = async ({
  file,
  data,
}: UploadCustomRequestOptions) => {
  const url = 'http://localhost:8088/uploadPersonalPdf'
  try {
    const formData = new FormData()
    formData.append('file', file.file as File, file.name)
    if (data) {
      Object.keys(data).forEach((key) => {
        formData.append(
          key,
          uploadData.value[key as keyof UploadCustomRequestOptions['data']],
        )
      })
    }
    const response = await fetch(url, {
      method: 'POST',
      body: formData,
    })
    if (!response.ok) {
      message.error('网络请求失败')
      return
    }
    const tmpData = await response.json()
    if (!tmpData || tmpData.code === 0) {
      message.error(`上传失败:${tmpData.msg}`)
      return
    }
    console.log('上传成功:', tmpData)
    return tmpData.data as IPdfMeta
  }
  catch (error) {
    message.error('上传失败')
    return null
  }
}
// 上传到问答知识库内
const uploadToChatHub = async (pdfMeta: IPdfMeta | null | undefined) => {
  if (!pdfMeta) {
    message.error('上传文件后，获取pdf元数据失败')
    return
  }

  // 提交到后端完成向量化
  const chatUrl = 'http://localhost:8088/upload_chat_hub'
  const chatResponse = await fetch(chatUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      url: pdfMeta.staticUrl,
      knowledge_base_name: pdfMeta.hubUuid,
    }),
  })
  if (!chatResponse.ok) {
    message.error('网络请求失败')
    return
  }
  const chatTmpData = await chatResponse.json()
  if (!chatTmpData || chatTmpData.code === 0) {
    message.error(`上传失败:${chatTmpData.msg}`)
    return
  }
  console.log(pdfMeta)
}

// 上传完成时的回调
const finishUpload = () => {
  // 隐藏遮罩层
  emit('setShowSpin', false)
  isShowAutoParseMeta.value = false
  isShowCustomMeta.value = false
  isShowUploadModal.value = false
  emit('refreshComponent')
}
const customUpload = async ({
  file,
  data,
}: UploadCustomRequestOptions) => {
  const pdfMeta: IPdfMeta | null | undefined = await uploadToPersonalHub({ file, data } as UploadCustomRequestOptions)
  await uploadToChatHub(pdfMeta)
  finishUpload()
}
</script>

<template>
  <div class="upload-pdf-component">
    <NButton v-if="props.isUserSelectSelf" type="primary" @click="openModal">
      上传个人PDF
    </NButton>

    <NModal
      v-model:show="isShowUploadModal" title="上传个人PDF"
      :style="{ width: '50%', height: '80vh', backgroundColor: '#f0f8ff' }"
    >
      <NSpace vertical align="center">
        <NUpload
          ref="upload" v-model:data="uploadData" accept=".pdf" directory-dnd :max="1" :default-upload="false"
          :custom-request="customUpload"
        >
          <NUploadDragger>
            <div class="upload-trigger">
              <div style="margin-bottom: 12px">
                <NIcon size="48" :depth="3">
                  <ArchiveIcon />
                </NIcon>
              </div>
              <NText style="font-size: 16px">
                点击按钮或者拖动文件到该区域来上传
              </NText>
              <NP depth="3" style="margin: 8px 0 0 0">
                请不要上传敏感数据，比如你的银行卡号和密码，信用卡号有效期和安全码
              </NP>
            </div>
          </NUploadDragger>
        </NUpload>
        <NSpace>
          <NButton type="primary" @click="isShowAutoParseMeta = true">
            自动解析元数据后上传
          </NButton>
          <NButton type="primary" @click="isShowCustomMeta = true">
            填写元数据后上传
          </NButton>
        </NSpace>
      </NSpace>
    </NModal>

    <NModal v-model:show="isShowAutoParseMeta" :style="{ backgroundColor: '#f0f8ff' }">
      <NSpace vertical>
        <NSpace justify="space-around" class="my-8">
          <div class="flex-1 text-center content-center">
            填写上传论文doi
          </div>
          <NInput v-model:value="editedPdfMeta.doi" />
        </NSpace>
        <NSpace justify="center" class="my-6">
          <NButton type="primary" @click="uploadFile(true)">
            确认上传
          </NButton>
        </NSpace>
      </NSpace>
    </NModal>

    <NModal
      v-model:show="isShowCustomMeta" title="填写元数据"
      :style="{ width: '50%', height: '80vh', backgroundColor: '#f0f8ff' }"
    >
      <NSpace vertical>
        <div class="flex-1 text-center text-xl my-4">
          填写元数据 <br> (可缺省)
        </div>
        <NDescriptions label-placement="left" label-align="left" :column="1" bordered label-style="width:12em">
          <!-- 自定义标签列表 -->
          <NDescriptionsItem label="自定义标签列表">
            <NSelect v-model:value="editedPdfMeta.tags" class="custom-tags" filterable multiple tag />
          </NDescriptionsItem>

          <!-- DOI -->
          <NDescriptionsItem label="DOI">
            <NInput v-model:value="editedPdfMeta.doi" />
          </NDescriptionsItem>
          <!-- 论文标题 -->
          <NDescriptionsItem label="论文标题">
            <NInput v-model:value="editedPdfMeta.title" />
          </NDescriptionsItem>
          <!-- 关键词 -->
          <NDescriptionsItem label="关键词">
            <NSelect v-model:value="editedPdfMeta.keyword" class="custom-tags" filterable multiple tag />
          </NDescriptionsItem>
          <!-- 作者 -->
          <NDescriptionsItem label="作者">
            <NSelect v-model:value="editedPdfMeta.author" class="custom-tags" filterable multiple tag />
          </NDescriptionsItem>
          <!-- 所在单位 -->
          <NDescriptionsItem label="所在单位">
            <NSelect v-model:value="editedPdfMeta.affiliation" class="custom-tags" filterable multiple tag />
          </NDescriptionsItem>
          <!-- 出版时间 -->
          <NDescriptionsItem label="出版时间">
            <NInput v-model:value="editedPdfMeta.publicTime" />
          </NDescriptionsItem>
          <!-- 出版刊物 -->
          <NDescriptionsItem label="出版刊物">
            <NInput v-model:value="editedPdfMeta.publicJournal" />
          </NDescriptionsItem>
          <!-- 是否是会议 -->
          <NDescriptionsItem label="是否是会议">
            <NSwitch v-model:value="editedPdfMeta.isConference" />
          </NDescriptionsItem>

          <!-- 会议名称 -->
          <NDescriptionsItem label="会议名称">
            <NInput v-model:value="editedPdfMeta.conferenceName" />
          </NDescriptionsItem>
          <!-- 会议时间 -->
          <NDescriptionsItem label="会议时间">
            <NInput v-model:value="editedPdfMeta.conferenceTime" />
          </NDescriptionsItem>
        </NDescriptions>
        <NSpace justify="center">
          <NButton type="primary" @click="uploadFile(false)">
            确认上传
          </NButton>
          <NButton type="primary" @click="isShowCustomMeta = false">
            取消上传
          </NButton>
        </NSpace>
      </NSpace>
    </NModal>
  </div>
</template>

<style scoped>
.upload-pdf-component {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.upload-trigger {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  border: 2px dashed #ccc;
  text-align: center;
  cursor: pointer;
}

.upload-trigger p {
  margin-top: 8px;
}
</style>
